﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace Benchmarks
{
    public class BenchmarkResults {
        private readonly int m_RunCount;
        private readonly long m_Ticks;
        private readonly long m_Millis;

        private readonly long m_EstimatedOverheadTicks;
        private readonly long m_EstimatedOverheadMillis;

        private readonly String m_BenchmarkName;
        private readonly String m_BenchmarkCategory;

        public BenchmarkResults( int run_count, long ticks, long millis, long estimated_overhead_ticks, long estimated_overhead_millis, string benchmark_name, string benchmark_category ) {
            m_RunCount = run_count;
            m_Ticks = ticks;
            m_Millis = millis;
            m_EstimatedOverheadTicks = estimated_overhead_ticks;
            m_EstimatedOverheadMillis = estimated_overhead_millis;
            m_BenchmarkName = benchmark_name;
            m_BenchmarkCategory = benchmark_category;
        }

        public long EstimatedOverheadTicks { get { return m_EstimatedOverheadTicks; } }
        public long EstimatedOverheadMillis { get { return m_EstimatedOverheadMillis; } }

        public int RunCount { get { return m_RunCount; } }
        public long Ticks { get { return m_Ticks; } }
        public long Millis { get { return m_Millis; } }
        public string BenchmarkCategory { get { return m_BenchmarkCategory; } }
        public string BenchmarkName { get { return m_BenchmarkName; } }
        public override string ToString() {

            //{category[right,10]}.{Name[right,20]}.{Iterations[Right,10]} TICKS:{Ticks[10]} MILLIS:{Millis[20])
            String FORMAT =
                //"{2,7} {0}" +
                //Environment.NewLine +
                "{3,10:N0} TICKS {4,6:N0} MILLIS - {2} {1}";
            
            return String.Format( FORMAT, 
                // skipped
                BenchmarkCategory, 
                BenchmarkName, 
                RunCount, 
                Ticks, 
                Millis );
        }
    }

    public abstract class Benchmark {
        public abstract String Category { get; }
        public abstract String Name { get; }
        public abstract void StartTrials();
        public abstract void RunOverheadTest( int num_iterations );
        public abstract void RunTrial( int num_iterations ); 
        public BenchmarkResults RunBenchmark( int num_iterations ) {
            StartTrials();

            Stopwatch stop = Stopwatch.StartNew();
            RunOverheadTest( num_iterations );
            stop.Stop();
            long overhead_ticks = stop.ElapsedTicks;
            long overhead_milli = stop.ElapsedMilliseconds;

            stop = Stopwatch.StartNew();
            RunTrial( num_iterations );
            stop.Stop();
            return new BenchmarkResults( num_iterations, stop.ElapsedTicks, stop.ElapsedMilliseconds, 
                overhead_ticks, overhead_milli,
                Name, Category );
        }
    }

    public class Benchmarker {
        
        public const int FIRST_RUN = 10;
        public const int SECOND_RUN = 10000;
        public const int THIRD_RUN = 1000000;
        public const int MAX = THIRD_RUN;
        public static List<BenchmarkResults> ExecuteSuite( BenchmarkSuite suite ) {
            List<BenchmarkResults> results = new List< BenchmarkResults >();
            foreach( Benchmark benchmark in suite.Benchmarks ) {
                results.Add( benchmark.RunBenchmark( FIRST_RUN ) );
            }
            foreach( Benchmark benchmark in suite.Benchmarks ) {
                results.Add( benchmark.RunBenchmark( SECOND_RUN ) );
            }
            foreach( Benchmark benchmark in suite.Benchmarks ) {
                results.Add( benchmark.RunBenchmark( THIRD_RUN ) );
            }
            return results;
        }

        public static List<BenchmarkResults> ExecuteBenchmark( Benchmark benchmark ) {
            List<BenchmarkResults> results = new List< BenchmarkResults >();
            results.Add(benchmark.RunBenchmark( FIRST_RUN ));
            results.Add(benchmark.RunBenchmark( SECOND_RUN ));
            results.Add(benchmark.RunBenchmark( THIRD_RUN ));
            return results;
        }
        
    }
}
